package com.leeoohoo.config;

import com.leeoohoo.common.UserContext;
import com.leeoohoo.common.UserHolder;
import com.leeoohoo.dto.MyUserDetails;
import com.leeoohoo.service.UserService;
import com.leeoohoo.utils.JwtTokenUtils;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.stream.Collectors;

@Slf4j
public class JwtSecurityContextRepository implements ServerSecurityContextRepository {

    private UserService userService;


    public JwtSecurityContextRepository(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
        return Mono.empty();
    }

    @Override
    public Mono<SecurityContext> load(ServerWebExchange exchange) {
        log.info("加载token:JwtSecurityContextRepository");
        String path = exchange.getRequest().getPath().toString();
        // 过滤路径
        if ("/login".equals(path)) {
            return Mono.empty();
        }
        String token = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.isNotBlank(token)) {
            // token能正常解析，表示token有效并对应数据库已知用户
            String subject = JwtTokenUtils.parseToken(token);
            Authentication newAuthentication = new UsernamePasswordAuthenticationToken(subject, subject);
            return new ReactiveAuthenticationManager() {
                @Override
                public Mono<Authentication> authenticate(Authentication authentication) {
                    Mono<MyUserDetails> userDetails = userService.findByToken(token, -1L);
                    // 如果对token有足够的安全认可，可以采用无状态凭证策略，将username和authorities放置在token串中解析获取，此处就可以不用查询数据库验证
//                    Mono<UserDetails> userDetails = userService.findByUsername(authentication.getPrincipal().toString());

                    return userDetails.map(it -> {
                        if (it == null) {
                            throw new DisabledException("账户不可用");
                        }
                        UserContext userContext = new UserContext();
                        userContext.setId(it.getId());
                        userContext.setUsername(it.getUsername());
                        userContext.setRuleList(it.getAuthorities()
                                .stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
                        UserHolder.setMyUser(userContext);
                        Authentication auth = new UsernamePasswordAuthenticationToken(userContext, null, it.getAuthorities());

                        return auth;
                    });
                }
            }.authenticate(newAuthentication).map(SecurityContextImpl::new);
        } else {
            return Mono.empty();
        }
    }
}

